package v1

import (
	"context"

	"quipus/internal/database/model"
	"quipus/internal/defs"
	"quipus/internal/utils"
	v1pb "quipus/proto/gen/api/v1"
	apb "quipus/proto/gen/assist"
	cpb "quipus/proto/gen/common"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/encoding/protojson"
)

func (s *APIV1Service) GetSystemConfig(ctx context.Context, settingKey apb.SystemSettingKey) string {
	settings, err := s.Gds.Handler.ListSystemSettings(ctx, settingKey.String())
	if err != nil {
		return ""
	}
	if len(settings) < 1 {
		return ""
	}

	return settings[0].Value
}

func (s *APIV1Service) GetSystemKnotRelatedSetting(ctx context.Context) (*v1pb.SystemKnotRelatedSetting, *model.SystemSetting) {
	sc := s.GetSystemConfig(ctx, apb.SystemSettingKey_KNOT_RELATED)
	return s.getSystemKnotRelatedSetting(sc)
}

func (s *APIV1Service) GetSystemStorageSetting(ctx context.Context) *v1pb.SystemStorageSetting {
	sc := s.GetSystemConfig(ctx, apb.SystemSettingKey_STORAGE)
	return s.getSystemStorageSetting(sc)
}

func (s *APIV1Service) getSystemKnotRelatedSetting(setting string) (*v1pb.SystemKnotRelatedSetting, *model.SystemSetting) {
	name := apb.SystemSettingKey_KNOT_RELATED
	knotRelatedSetting := &v1pb.SystemKnotRelatedSetting{}
	if len(setting) > 0 {
		err := protojson.Unmarshal([]byte(setting), knotRelatedSetting)
		if err != nil {
			return nil, nil
		}
	}

	if knotRelatedSetting.ContentLengthLimit < defs.DefaultContentLengthLimit {
		knotRelatedSetting.ContentLengthLimit = defs.DefaultContentLengthLimit

		systemSetting := &model.SystemSetting{
			Name: name.String(),
		}
		b, err := protojson.Marshal(knotRelatedSetting)
		if err != nil {
			return nil, nil
		}
		systemSetting.Value = string(b)
		return knotRelatedSetting, systemSetting
	}

	return knotRelatedSetting, nil
}

func (s *APIV1Service) getSystemGeneralSetting(setting string) *v1pb.SystemGeneralSetting {
	generalSetting := &v1pb.SystemGeneralSetting{}
	if len(setting) > 0 {
		err := protojson.Unmarshal([]byte(setting), generalSetting)
		if err != nil {
			return nil
		}
	}
	return generalSetting
}

func (s *APIV1Service) getSystemBasicSetting(setting string) *v1pb.SystemBasicSetting {
	basicSetting := &v1pb.SystemBasicSetting{}
	if len(setting) > 0 {
		err := protojson.Unmarshal([]byte(setting), basicSetting)
		if err != nil {
			return nil
		}
	}
	return basicSetting
}

func (s *APIV1Service) getSystemStorageSetting(setting string) *v1pb.SystemStorageSetting {
	storeSetting := &v1pb.SystemStorageSetting{}
	if len(setting) > 0 {
		err := protojson.Unmarshal([]byte(setting), storeSetting)
		if err != nil {
			return nil
		}
	}
	return storeSetting
}

func (s *APIV1Service) getSystemValue(ctx context.Context, systemSetting *v1pb.SystemSetting, systemSettingKey apb.SystemSettingKey) error {

	sc := s.GetSystemConfig(ctx, systemSettingKey)
	modelConfig, err := s.setSysValue(sc, systemSetting, systemSettingKey)
	if modelConfig != nil {
		s.Gds.Handler.UpsertSystemSetting(ctx, modelConfig)
	}
	return err
}

func (s *APIV1Service) setSysValue(sc string, systemSetting *v1pb.SystemSetting, systemSettingKey apb.SystemSettingKey) (modelconfig *model.SystemSetting, err error) {
	modelconfig = nil
	switch systemSettingKey {
	case apb.SystemSettingKey_BASIC:
		setting := s.getSystemBasicSetting(sc)
		systemSetting.Value = &v1pb.SystemSetting_BasicSetting{BasicSetting: setting}
	case apb.SystemSettingKey_GENERAL:
		setting := s.getSystemGeneralSetting(sc)
		systemSetting.Value = &v1pb.SystemSetting_GeneralSetting{GeneralSetting: setting}
	case apb.SystemSettingKey_KNOT_RELATED:
		setting, m := s.getSystemKnotRelatedSetting(sc)
		modelconfig = m
		systemSetting.Value = &v1pb.SystemSetting_KnotRelatedSetting{KnotRelatedSetting: setting}
	case apb.SystemSettingKey_STORAGE:
		setting := s.getSystemStorageSetting(sc)
		systemSetting.Value = &v1pb.SystemSetting_StorageSetting{StorageSetting: setting}

	default:
		err = status.Errorf(codes.InvalidArgument, "unsupported workspace setting key: %v", systemSettingKey)
	}
	return
}

func (s *APIV1Service) GetSystemSetting(ctx context.Context, request *v1pb.GetSystemSettingRequest) (*v1pb.SystemSetting, error) {
	systemSettingKeyString, err := ExtractTokenFromUrn(utils.SystemSettingUrnPrefix, request.Urn)
	if err != nil {
		return nil, status.Errorf(codes.InvalidArgument, "invalid workspace setting name: %v", err)
	}

	systemSettingKey := apb.SystemSettingKey(apb.SystemSettingKey_value[systemSettingKeyString])

	systemSetting := &v1pb.SystemSetting{
		// Key: systemSettingKey,
		Urn: request.Urn,
	}

	err = s.getSystemValue(ctx, systemSetting, systemSettingKey)

	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to get workspace setting: %v", err)
	}

	// For storage setting, only host can get it.
	if systemSettingKey == apb.SystemSettingKey_STORAGE {
		user, err := s.GetCurrentUser(ctx)
		if err != nil {
			return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
		}

		if user == nil || user.Role != cpb.Role_HOST.String() {
			return nil, status.Errorf(codes.PermissionDenied, "permission denied")
		}
	}

	return systemSetting, nil
}

func (s *APIV1Service) SetSystemSetting(ctx context.Context, request *v1pb.SetSystemSettingRequest) (*v1pb.SystemSetting, error) {
	// if s.Profile.Mode == "demo" {
	// 	return nil, status.Errorf(codes.InvalidArgument, "setting workspace setting is not allowed in demo mode")
	// }

	user, err := s.GetCurrentUser(ctx)
	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
	}
	if user.Role != cpb.Role_HOST.String() {
		return nil, status.Errorf(codes.PermissionDenied, "permission denied")
	}

	setting := convertSystemSettingToStore(request.Setting)
	err = s.Gds.Handler.UpsertSystemSetting(ctx, setting)
	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to upsert workspace setting: %v", err)
	}

	systemSetting := &v1pb.SystemSetting{
		Urn: request.Setting.Urn,
	}

	s.setSysValue(setting.Value, systemSetting, apb.SystemSettingKey(apb.SystemSettingKey_value[request.Setting.Urn]))

	return systemSetting, nil
}

func convertSystemSettingToStore(setting *v1pb.SystemSetting) *model.SystemSetting {
	settingKeyString, e := ExtractTokenFromUrn(utils.SystemSettingUrnPrefix, setting.Urn)
	if e != nil {
		return nil
	}

	var sc string
	var sb []byte
	sysKey := apb.SystemSettingKey(apb.SystemSettingKey_value[settingKeyString])

	switch sysKey {
	case apb.SystemSettingKey_GENERAL:
		value := setting.GetGeneralSetting()
		sb, _ = protojson.Marshal(value)
	case apb.SystemSettingKey_STORAGE:
		value := setting.GetStorageSetting()
		sb, _ = protojson.Marshal(value)
	case apb.SystemSettingKey_KNOT_RELATED:
		value := setting.GetKnotRelatedSetting()
		sb, _ = protojson.Marshal(value)
	}
	sc = string(sb)

	systemSetting := &model.SystemSetting{
		Name:  settingKeyString,
		Value: string(sc),
	}
	return systemSetting
}
